#include "llOption.hpp"
#include "llSystemInfo.hpp"
#include "llMakeDirectory.hpp"
#include "llReporter.hpp"
#include "llDependence.hpp"
using namespace llmake;

#include <stdlib.h>

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>

extern shared_ptr<llSystemInfo> global_SystemInfo;
extern shared_ptr<llOption> global_Option;
extern shared_ptr<llMakeDirectory> global_Root;
//shared_ptr<llDependenceEngine>	global_DependenceEngine;

llExecuteCommands llMakeDirectory::m_ExecCommand;
llTargetMap llMakeDirectory::m_Target;


llMakeDirectory::llMakeDirectory(const string &folder)
 :m_Folder(folder)
 ,m_ReleaseCflags(" -O3 -Wall ")
 ,m_ReleaseCxxflags(" -O3 -Wall ")
 ,m_ReleaseLdflags("")
 ,m_DebugCflags(" -g -Wall ")
 ,m_DebugCxxflags(" -g -Wall ")
 ,m_DebugLdflags("")
// ,m_Top(shared_ptr<llMakeDirectory>(NULL))
{
	string fileName;
	if (global_Option->m_CMake)
	{
		fileName = m_Folder + "/CMakeLists.txt";
	}
	else
	{
		fileName = m_Folder + "/LLMakeLists.txt";
	}
	m_TokenEngine = shared_ptr<llTokenEngine>( new llTokenEngine(fileName));
	llTokenList tokens;
	m_TokenEngine->getToken(tokens);
	m_LexEngine = shared_ptr<llLexEngine>(new llLexEngine(tokens));
	llLexList lexs;
	m_LexEngine->getLex(lexs);
	m_SyntaxEngine = shared_ptr<llSyntaxEngine>(new llSyntaxEngine(lexs));

	if(global_SystemInfo->m_MINGW)
	{
		m_ReleaseCflags += " -D__MINGW32__ ";
		m_ReleaseCxxflags += " -D__MINGW32__ ";
//		m_ReleaseLdflags +=;
		m_DebugCflags += " -D__MINGW32__ ";
		m_DebugCxxflags += " -D__MINGW32__ ";
//		m_DebugLdflags +=;
	}
}
llMakeDirectory::~llMakeDirectory()
{
	cout<<__FUNCTION__<<endl;
}

void llMakeDirectory::doParse(void)
{

	llCommandList commands;
	m_SyntaxEngine->setOwner(shared_from_this());
	m_SyntaxEngine->getCommand(commands);

	m_ParseCommandEngine = shared_ptr<llParseCommandEngine>(new llParseCommandEngine(commands));

	m_ParseCommandEngine->doParseCommand();
	if( NULL !=  m_Top )
	{
		return;
	}
	
	llTargetMap::iterator ir = m_Target.begin();
	for(; m_Target.end() != ir;ir++ )
	{
		llTargetNode target = ir->second;
		list<string>::iterator typeIr = target.m_Type.begin();
		for(; target.m_Type.end() != typeIr ;typeIr++)
		{
			llExecuteCommandNode bc_ld ;
			llExecuteCommandNode bc_opt;
			llExecuteCommandNode bc_llc;
			llExecuteCommandNode bc_mv ;
			llExecuteCommandNode native_as;
			llExecuteCommandNode native_ld;
			llExecuteCommandNode strip;
			bc_ld.m_Body = target.m_Path;
			bool mvFlag = false;
			bool stripFlag = false;
			if ( "EXEC"== *typeIr )
			{
				bc_ld.m_Command="${BC_LD_EXE}";
				bc_ld.m_Body += " -o " + global_SystemInfo->m_PWD + "/llvm-target/" +target.m_Name;
				if( global_SystemInfo->m_MINGW )
				{
					mvFlag= true;
					bc_mv.m_Command="${CP}";
					bc_mv.m_Body = global_SystemInfo->m_PWD + "/llvm-target/" +target.m_Name+".exe.bc ";
					bc_mv.m_Body += global_SystemInfo->m_PWD + "/llvm-target/" +target.m_Name+".bc";

					stripFlag = true;
					strip.m_Command="${STRIP}";
					strip.m_Body = global_SystemInfo->m_PWD + "/native-target/" +target.m_Name+".exe";
				}
				bc_opt.m_Command="${BC_OPT}";
				bc_opt.m_Body = global_SystemInfo->m_PWD + "/llvm-target/" +target.m_Name+".bc";
				bc_opt.m_Body += " -o " + global_SystemInfo->m_PWD + "/llvm-target/" +target.m_Name+".bc";

				bc_llc.m_Command="${BC_LLC}";
				bc_llc.m_Body = global_SystemInfo->m_PWD + "/llvm-target/" +target.m_Name+".bc";
				bc_llc.m_Body += " -o " + global_SystemInfo->m_PWD + "/native-target/" +target.m_Name+".s";

				native_as.m_Command="${NATIVE_AS}";
				native_as.m_Body = global_SystemInfo->m_PWD + "/native-target/" +target.m_Name+".s";
				native_as.m_Body += " -o " + global_SystemInfo->m_PWD + "/native-target/" +target.m_Name+".o";
				
				native_ld.m_Command="${NATIVE_CXX_LD}";
				native_ld.m_Body = global_SystemInfo->m_PWD + "/native-target/" +target.m_Name+".o";
				native_ld.m_Body += " -o " + global_SystemInfo->m_PWD + "/native-target/" +target.m_Name;
				native_ld.m_Body += " -L" + global_SystemInfo->m_PWD + "/native-target";
				list<string>::iterator libIt= target.m_LinkLibs.begin();
				for( ; target.m_LinkLibs.end() != libIt ; libIt++ )
				{
					native_ld.m_Body += " -l" + *libIt;
				}
			}
			if ( "STATIC"== *typeIr )
			{
				bc_ld.m_Command="${BC_LD_LIB}";
				bc_ld.m_Body += " -o " + global_SystemInfo->m_PWD + "/llvm-target/"+"lib" +target.m_Name+".bc";

				bc_opt.m_Command="${BC_OPT}";
				bc_opt.m_Body = global_SystemInfo->m_PWD + "/llvm-target/"+"lib" +target.m_Name+".bc";
				bc_opt.m_Body += " -o " + global_SystemInfo->m_PWD + "/llvm-target/"+"lib" +target.m_Name+".bc";

				bc_llc.m_Command="${BC_LLC}";
				bc_llc.m_Body = global_SystemInfo->m_PWD + "/llvm-target/"+"lib" +target.m_Name+".bc";
				bc_llc.m_Body += " -o " + global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name+".s";

				native_as.m_Command="${NATIVE_AS}";
				native_as.m_Body = global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name+".s";
				native_as.m_Body += " -o " + global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name+".o";
				
				native_ld.m_Command="${NATIVE_AR}";
				native_ld.m_Body = " r " + global_SystemInfo->m_PWD + "/native-target/"+"lib" + target.m_Name + ".a ";
				native_ld.m_Body += global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name+".o";
			}
			if ( "SHARED"== *typeIr )
			{
				bc_ld.m_Command="${BC_LD_LIB}";
				bc_ld.m_Body += " -o " + global_SystemInfo->m_PWD + "/llvm-target/"+"lib" +target.m_Name+".bc";
				
				bc_opt.m_Command="${BC_OPT}";
				bc_opt.m_Body = global_SystemInfo->m_PWD + "/llvm-target/"+"lib" +target.m_Name+".bc";
				bc_opt.m_Body += " -o " + global_SystemInfo->m_PWD + "/llvm-target/"+"lib" +target.m_Name+".bc";

				bc_llc.m_Command="${BC_LLC}";
				bc_llc.m_Body = global_SystemInfo->m_PWD + "/llvm-target/"+"lib" +target.m_Name+".bc";
				bc_llc.m_Body += " -o " + global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name+".s";

				native_as.m_Command="${NATIVE_AS}";
				native_as.m_Body = global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name+".s";
				native_as.m_Body += " -o " + global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name+".o";
				
				native_ld.m_Command="${NATIVE_CXX_LD}";
				native_ld.m_Body = global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name+".o";
				native_ld.m_Body += " -shared -o " + global_SystemInfo->m_PWD + "/native-target/"+"lib" +target.m_Name +".so";
				
			}
			m_ExecCommand.push_back( bc_ld );
			if(mvFlag)
			{
				m_ExecCommand.push_back( bc_mv );
			}
			m_ExecCommand.push_back( bc_opt );
			m_ExecCommand.push_back( bc_llc );
			m_ExecCommand.push_back( native_as );
			m_ExecCommand.push_back( native_ld );
			if(stripFlag)
			{
				m_ExecCommand.push_back( strip );
			}
		}
	}
}

static boost::mutex globalExecMtex;
static boost::mutex globalIoMtex;
static bool globalExceResult = true;
static void doExecuteThreadProc(const string &cmd)
{
	if( false == global_Option->m_IgnoreError && false == globalExceResult )
	{
		return;
	}
	{
		boost::mutex::scoped_lock lock(globalIoMtex);
		cout<<"execute -> "<<cmd<<endl;
	}
	int ret = system(cmd.c_str());
	if(ret)
	{
		boost::mutex::scoped_lock lock(globalExecMtex);
		globalExceResult = false;
	}
}

typedef shared_ptr<boost::thread > threadPtr;
void llMakeDirectory::doExecute(void)
{
	llExecuteCommandNode mkdir = {"${MKDIR}",""};
	mkdir.m_Body = global_SystemInfo->m_PWD + "/Bytecode";
	m_ExecCommand.push_front( mkdir );

	mkdir.m_Body = global_SystemInfo->m_PWD + "/llvm-target";
	m_ExecCommand.push_front( mkdir );

	mkdir.m_Body = global_SystemInfo->m_PWD + "/native-target";
	m_ExecCommand.push_front( mkdir );

	shared_ptr<llDependenceEngine>dependence (new llDependenceEngine(global_SystemInfo->m_PWD + "/Bytecode"));
	dependence->load();
	llExecuteCommands::iterator itr = m_ExecCommand.begin();
//	system("cd");
	bool err = false;
	
	list<threadPtr> threadList;
	for(; m_ExecCommand.end() != itr;itr++)
	{
		if( false == dependence->parseDependence( itr->m_Dst ,itr->m_Src ))
		{
			continue;
		}
		if ( "${WAIT}" == itr->m_Command )
		{
//			list<boost::thread>::iterator threadIt = threadList;
			while( false == threadList.empty() )
			{
				threadList.front()->join();
				threadList.pop_front();
			}
		}
		else if ( ( "${CC}" == itr->m_Command ) || ("${CXX}" == itr->m_Command ) )
		{
			string cmd = global_SystemInfo->getSystemCommand(itr->m_Command);
			cmd += " " + itr->m_Body;
			threadPtr thrd( new boost::thread(boost::bind(&doExecuteThreadProc, cmd) ));
			threadList.push_back(thrd);
		}
		else
		{
			string cmd = global_SystemInfo->getSystemCommand(itr->m_Command);
			cmd += " " + itr->m_Body;
			doExecuteThreadProc(cmd);
		}
	}
	dependence->save();
	if( false == globalExceResult )
	{
		llReporter error("make failure");
		error.Error();
	}
}

void llMakeDirectory::insertVar( const string &name,const llTokenList &vars)
{
	llTokenList local = vars;
#ifdef DEBUG			
	cout<<"name ["<<name<<" ]";
	llTokenList::iterator itToken = local.begin();
	cout<<" tokens (";
	for( ; local.end() != itToken;itToken++)
	{
		cout<<"<"<<itToken->m_Token<<">";
	}
	cout<<")"<<endl;
#endif
	this->extractTokensVars(local);
	this->jointTokens(local);
#ifdef DEBUG			
	cout<<"-----------name ["<<name<<" ]";
	itToken = local.begin();
	cout<<" tokens (";
	for( ; local.end() != itToken;itToken++)
	{
		cout<<"<"<<itToken->m_Token<<">";
	}
	cout<<")"<<endl;
#endif
	llVariableMap::iterator ir = m_Vars.find(name);
	if( m_Vars.end() != ir )
	{
		llReporter warn(" Overwrite old values");
		warn.Warning();
		m_Vars.erase(ir);
	}
	m_Vars.insert(llVariableMap::value_type(name,local));
}

void llMakeDirectory::getVar(const string &name,llTokenList &vars)
{
	llVariableMap::iterator ir = m_Vars.find(name);
	if( m_Vars.end() != ir )
	{
		vars = ir->second;
	}
}

void llMakeDirectory::insertMacroAndFunction(const string &name,const llMacroAndFunctionNode &macro_func)
{
	m_MacroAndFunction.insert(llMacroAndFunctionMap::value_type(name,macro_func));
}
bool llMakeDirectory::findMacroAndFunction(const string &name,llMacroAndFunctionNode &macro_func)
{
	llMacroAndFunctionMap::iterator itr = m_MacroAndFunction.find(name);
	if( m_MacroAndFunction.end() == itr )
	{
		return false;
	}
	else
	{
		macro_func = itr->second;
		return true;
	}
}


void llMakeDirectory::replaceCommandLists(llCommandList &commandsSrc,llCommandList &commandsDst,llVariableMap &varMap)
{
	llCommandList::iterator itr = commandsSrc.begin();
	for(;commandsSrc.end() != itr;itr++)
	{
		llTokenList newTokens;
//		llTokenList::iterator itrToken = (*itr)->m_Tokens.begin();
		llTokenList oldTokens = (*itr)->m_Tokens;
		while( false == oldTokens.empty() )
		{
			llTokenList::iterator itrToken = oldTokens.begin();
			if( "$" == itrToken->m_Token )
			{
				itrToken++;
				itrToken++;
				llVariableMap::iterator repItr = varMap.find(itrToken->m_Token);
				if( varMap.end() != repItr )
				{
					llTokenList toRepTokens = repItr->second;
					llTokenList::iterator varItr = toRepTokens.begin();
					for(; toRepTokens.end()!= varItr;varItr++)
					{
						newTokens.push_back( *varItr );
					}
				}
				else
				{
					llTokenList::iterator itrTokenStart = oldTokens.begin();
					newTokens.push_back( *itrTokenStart );
					itrTokenStart++;
					newTokens.push_back( *itrTokenStart );
					itrTokenStart++;
					newTokens.push_back( *itrTokenStart );
					itrTokenStart++;
					newTokens.push_back( *itrTokenStart );
				}
				oldTokens.pop_front();
				oldTokens.pop_front();
				oldTokens.pop_front();
				oldTokens.pop_front();
			}
			else
			{
				newTokens.push_back( *itrToken );
				oldTokens.pop_front();
			}
		}
		(*itr)->m_Tokens = newTokens;
		commandsDst.push_back(*itr);
	}
}
void llMakeDirectory::extractTokensVars(llTokenList & tokens)
{
	llTokenList dst;
	while( false == tokens.empty() )
	{
		llTokenNode cur = tokens.front();
		if ( "$" == cur.m_Token )
		{
			tokens.pop_front();//"$"
			tokens.pop_front();//"{"
			llTokenNode var = tokens.front();
			string name = var.m_Token;
			llVariableMap::iterator ir = m_Vars.find(name);
			if( m_Vars.end() == ir )
			{
				llReporter warn(" var not defined " + name );
//				warn.append(tokens);
				warn.Warning();
			}
			else
			{
				llTokenList repList = ir->second;
				llTokenList::iterator itrRep = repList.begin();
				for( ; repList.end() != itrRep ; itrRep++ )
				{
					dst.push_back(*itrRep);
				}
			}
			tokens.pop_front();//"var"
			llTokenNode right = tokens.front();
//			cout<<"right "<<right.m_Token<<":"<<right.m_Joint<<endl;
			if ( right.m_Joint && false == dst.empty() )
			{
				llTokenNode rep = dst.back();
				dst.pop_back();
				rep.m_Joint = true;
				dst.push_back(rep);
			}
			tokens.pop_front();//"}"
		}
		else
		{
			dst.push_back(cur);
			tokens.pop_front();
		}
	}
//	cout<<"m_Tokens.size() ["<<m_Tokens.size()<<"]"<<endl;
	tokens = dst;
}


void llMakeDirectory::jointTokens(llTokenList & tokens)
{
	llTokenList dst;
	string joint("");
	while( false == tokens.empty() )
	{
		llTokenNode cur = tokens.front();
		if( cur.m_Joint && 1 < tokens.size())
		{
			joint += cur.m_Token;
		}
		else
		{
			cur.m_Token = joint + cur.m_Token;
			dst.push_back(cur);
			joint = "";
		}
		tokens.pop_front();
	}
	tokens = dst;
}
#if 0
string llMakeDirectory::getFlags(const string & type)
{
	string flags("");
	if ( "cflag"== type )
	{
		flags = m_Cflags;
	}
	if ( "cxxflag"== type )
	{
		flags = m_Cxxflags;
	}
	if ( "ldflag"== type )
	{
		flags = m_Ldflags;
	}
	if( NULL != m_Top)
	{
		flags += m_Top->getFlags(type);
	}
	else
	{
		if ( "cflag"== type )
		{
			flags += m_ReleaseCflags;
		}
		if ( "cxxflag"== type )
		{
			flags += m_ReleaseCxxflags;
		}
		if ( "ldflag"== type )
		{
			flags += m_ReleaseLdflags;
		}
	}
	return flags;
}
#else
string llMakeDirectory::getFlags(const string & type)
{
	string flags("");
	if ( "cflag"== type )
	{
		flags = m_Cflags;
		flags += m_ReleaseCflags;
	}
	if ( "cxxflag"== type )
	{
		flags = m_Cxxflags;
		flags += m_ReleaseCxxflags;
	}
	if ( "ldflag"== type )
	{
		flags = m_Ldflags;
		flags += m_ReleaseLdflags;
	}
	return flags;
}
#endif


void llMakeDirectory::insertFlags(const string & type,const string &flags)
{
	if ( "cflag"== type )
	{
		m_Cflags += " " + flags;
	}
	if ( "cxxflag"== type )
	{
		m_Cxxflags  += " " + flags;
	}
	if ( "ldflag"== type )
	{
		m_Ldflags +=  " " + flags;
	}
}


void llMakeDirectory::insertExecuteCommand(const llExecuteCommandNode & comand)
{
	m_ExecCommand.push_back(comand);
}

void llMakeDirectory::insertTarget(const string &name,const llTargetNode &target)
{
	m_Target.insert(llTargetMap::value_type(name,target));
}

bool llMakeDirectory::appendTargetLink(const string &name,list<string> &libs)
{
	llTargetMap::iterator it = m_Target.find(name);
	if(m_Target.end() != it )
	{
		list<string>::iterator libIt= libs.begin();
		for( ; libs.end() != libIt ; libIt++ )
		{
			it->second.m_LinkLibs.push_back(*libIt);
		}
		return true;
	}
	else
	{
		return false;
	}
}
